home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-16 / slowdown.zip / TEST / WHOA / WHOA!.ASM next >
Assembly Source File  |  1988-01-19  |  33KB  |  1,161 lines

  1. page            66,132
  2. name            WHOA
  3. Title            WHOA! Version 1.0 - Brad D Crandall, 1987
  4. ;            Copyright 1988 COMPUTE! Publications, Inc.
  5. ;            All rights reserved.
  6. ;------------------------------------------------------------------------------
  7. ;    WHOA!.asm    -    Brad D Crandall
  8. ;                
  9. ;
  10. ;    This program will slow a computer down so programs and
  11. ;    games can be examined closely. Designed to run in a Text
  12. ;    or graphic screen mode. To bring up WHOA!'s Menu,
  13. ;    press <Alt> <F10>.
  14. ;
  15. ;    Compiler:    MASM Version 2.0 or greater.
  16. ;    Computer:    IBM PC or Compatible running on MS-DOS or
  17. ;            PC-DOS Version 2.0 or greater. Also works
  18. ;            on IBM's Personal System/2s under DOS 3.3.
  19. ;
  20. ;    Compiling procedure:
  21. ;            masm WHOA!;
  22. ;            link WHOA!;
  23. ;            exe2bin WHOA! WHOA!.com
  24. ;
  25. ;    Notes:
  26. ;        Care should be taken when determining what value to use for
  27. ;        SET_MAX (located in EQUATE AREA). If the value is too large,  
  28. ;        a hard loop may result. The value 435 if optimum for a 12-MHz
  29. ;        machine. Slower machines will need a slightly lower value.
  30. ;
  31. ;        Special consideration has been taken to allow WHOA to be 
  32. ;        compatible with DOS versions earlier than 3.1, which do not
  33. ;        support the MULTIPLEX interrupt protocol.  WHOA uses the 
  34. ;        multiplex interrupt because it provides a broader range of 
  35. ;        software compatibility than do the eight conventional user 
  36. ;        interrupts.
  37. ;
  38. ;        For older versions of DOS, WHOA uses a user interrupt
  39. ;        (SLOW_INT) as the channel of communication to the resident
  40. ;        portion of WHAO!. To provide full compatibility, SLOW_INT
  41. ;        should be set to an interrupt that is not used by any of the 
  42. ;        software you wish to use WHOA with.
  43. ;
  44. ;------------------------------------------------------------------------------
  45.  
  46. ;------------------------------------------------------------------------------
  47. ;                E Q U A T E   A R E A
  48. ;------------------------------------------------------------------------------
  49. MULTI_INT        equ    2fh    ; Multiplex used if DOS >= 3.1.
  50. SLOW_INT        equ    60h    ; Interrupt used if DOS < 3.1,
  51.                     ; other possible values 60h-67h.
  52. KEYBOARD_INT        equ    16h    ; Keyboard Interrupt.
  53. TIME_INT        equ    08h    ; Time of Day Interrupt.
  54. MULTI_HANDLE        equ    89h    ; Our Unique Multiplex ID.
  55. INSTALL_CHECK        equ    0h    ; Code to check if resident.
  56. UNINSTALL        equ    1h    ; Code to uninstall WHOA!
  57. SET_LOOP        equ    2h    ; Code to set the delay loop.
  58.  
  59. STACK_SIZE        equ    100h    ; Size for our Stack.
  60.  
  61. HOT_KEY            equ    7100h    ; ALT F10.
  62. STROBE_PAUSE        equ    5    ; Strobe delay count setting.
  63. SET_MIN            equ    1    ; Minimum delay loop value.
  64. SET_MAX            equ    435    ; Maximum delay loop value.
  65. TIME_PAUSE        equ    100    ; Inner loop initial setting.
  66. LOOP_DEFAULT        equ    350    ; Delay default setting.
  67.  
  68. STROBE_BEGIN        equ    28    ; Column coordinate for strobe.
  69. STROBE_END        equ    37    ; Column coordinate for strobe.
  70.  
  71. SCREEN_SIZE        equ    16 * 1024 / 2 ; Size in words of largest
  72.                     ; screen to save.
  73.  
  74. BORDER            =    16+14    ; Border color.
  75. TEXT            =    16+15    ; Text color.
  76.  
  77. DOS_CMP            equ    0310h    ; DOS comparison constant.
  78.  
  79.             ;--- Return Codes ---
  80. NOT_INSTALLED        equ    0    ; WHOA not installed return code.
  81. INSTALLED        equ    0ffh    ; WHOA is installed return code.
  82. ERROR            equ    0feh    ; Error occurred
  83. OK            equ    0fdh    ; No errors occurred.
  84.  
  85. ;------------------------------------------------------------------------------
  86. ;                 M A C R O   A R E A
  87. ;------------------------------------------------------------------------------
  88.  
  89.             ;----------------------------------------------------
  90.             ;    DISPLAY_AT parm1,parm2,parm3,parm4 - Display
  91.             ;    message on screen starting at fixed 
  92.             ;    location. Does not use DOS.
  93.             ;
  94.             ;    parm1 = X coordinate of start of message.
  95.             ;    parm2 = Y coordinate of start of message.
  96.             ;    parm3 = Color of message.
  97.             ;    parm4 = String of character to display.
  98.             ;----------------------------------------------------
  99. DISPLAY_AT        macro    parm1,parm2,parm3,parm4
  100.             local    label1,label2
  101.             jmp    label2
  102. label1            db    parm4&,0
  103. label2:            mov    si,offset cseg:label1
  104.             mov    cx,&parm3
  105.             mov    ax,&parm2
  106.             mov    di,&parm1
  107.             call    display_at_proc
  108.             endm
  109.  
  110.             ;---------------------------------------------------
  111.             ;    DISPLAY_DOS parm1 - Display a message to the
  112.             ;    console. Uses DOS function 09h.
  113.             ;
  114.             ;    parm1 = Message to display.
  115.             ;---------------------------------------------------
  116. DISPLAY_DOS        macro    parm1
  117.             local    label1,label2
  118.             jmp    label2
  119. label1            db    parm1&,24h
  120. label2:            mov    dx,offset cseg:label1
  121.             mov    ah,09h
  122.             int    21h
  123.             endm
  124.  
  125.             ;-----------------------------------------------------
  126.             ;    RESIDENT parm1 - Executes the resident portion
  127.             ;    of WHOA! Determines which interrupt to 
  128.             ;    use (multiplex or user) based on DOS version.
  129.             ;
  130.             ;    parm1 - Resident function.
  131.             ;-----------------------------------------------------
  132. RESIDENT        macro    parm1
  133.             local    label1,label2
  134.             mov    al,&parm1
  135.             cmp    cs:dos_version,DOS_CMP    ; What DOS Version??
  136.             jae    label1            ; Jump if DOS >= 3.1.
  137.             call    call_interrupt
  138.             jmp    label2
  139. label1:            mov    ah,MULTI_HANDLE
  140.             int    MULTI_INT
  141. label2:
  142.             endm
  143.  
  144. ;------------------------------------------------------------------------------
  145. ;                C O D E   A R E A
  146. ;------------------------------------------------------------------------------
  147. cseg            segment    byte public 'code'
  148.             assume    cs:cseg,ds:cseg
  149.             org    100h
  150. slowdown        proc    far
  151.             jmp    slowdown_0010
  152.  
  153. ;------------------------------------------------------------------------------
  154. ;                T S R   D A T A   A R E A
  155. ;------------------------------------------------------------------------------
  156. dos_version        dw    ?    ; Version number of DOS.
  157. slow_interrupt        db    SLOW_INT ; Interrupt used if DOS < 3.1.
  158. old_multi_int        dd    ?    ; Address of old multiplex.
  159. old_keyboard_int    dd    ?    ; Address of old keyboard.
  160. old_time_int        dd    ?    ; Address of old time of day.
  161. activated        db    0    ; Switch to prevent re-entry.
  162. crt_stat        dw    ?    ; CRT port.
  163.  
  164. save_sp            dw    ?    ; Save area of old stack.
  165. save_ss            dw    ?    ; Save area of old stack.
  166. video_state        db    ?    ; Save video state.
  167. cursor_location        dw    ?    ; Save cursor location.
  168. current_page        db    ?    ; Save page.
  169. current_background    db    ?    ; Current background color.
  170. current_palette        db    ?
  171.  
  172. asc_value        db    6 dup(0) ; Area for number display.
  173. setting            dw    LOOP_DEFAULT ; Delay loop setting.
  174. delay_switch        db    1    ; Delay Switch on/off. 1 = ON.
  175.  
  176. strobe            dw    STROBE_BEGIN ; Location of strobe pattern.
  177. move_strobe1        dw    ?    ; Delay countdown.
  178. move_strobe2        dw    ?    ; Delay countdown.
  179. screen_seg        dw    ?    ; Address of screen.
  180. enable            db    1    ; Enable/disable switch.
  181. columns            db    80    ; Number of columns on screen.
  182.  
  183. ;------------------------------------------------------------------------------
  184. ;               T S R   C O D E   A R E A
  185. ;------------------------------------------------------------------------------
  186.  
  187.             ;-------------------------------------------------------
  188.             ;   multiplex - This procedure handles external
  189.             ;    communications for the memory resident
  190.             ;    portion of WHOA running on DOS 3.1 or 
  191.             ;    greater.
  192.             ;-------------------------------------------------------
  193. multiplex        proc    far
  194.  
  195.             ;--- Check for our unique handle. If not found ---
  196.             ;--- jump to next multiplex routine in chain. ----
  197.  
  198.             cmp    ah,MULTI_HANDLE 
  199.             je    multiplex_0010  
  200.             jmp    dword ptr cs:old_multi_int
  201. multiplex_0010:        call    request     
  202.             iret    ; Must return as an interrupt routine.
  203. multiplex        endp
  204.  
  205.             ;-------------------------------------------------
  206.             ;   interrupt - This procedure handles external
  207.             ;    communications for the memory resident
  208.             ;    portion of WHOA running on DOS 3.0 or
  209.             ;    less.
  210.             ;-------------------------------------------------
  211. interrupt        proc    far
  212.             call    request
  213.             iret
  214. interrupt        endp
  215.  
  216.             ;-----------------------------------------------
  217.             ;   request - This routine execute a slowdown
  218.             ;    function based on the contents of AL. 
  219.             ;
  220.             ;   Status returned in AL.
  221.             ;-----------------------------------------------
  222. request            proc    near
  223.             ;--- We are being called. Find out why ---
  224.  
  225. request_0010:        cmp    al,INSTALL_CHECK ; Check for installed?
  226.             jne    request_0020    ; Jump if NO.   
  227.             mov    al,INSTALLED    ; We are already installed.
  228.             ret
  229.  
  230. request_0020:        cmp    al,UNINSTALL    ; Ordered to uninstall?
  231.             jne    request_0030    ; Jump if NO.   
  232.             call    restore        ; Uninstall.
  233.             mov    al,OK        ; Everything went OK.
  234.             ret
  235.  
  236. request_0030:        cmp    al,SET_LOOP    ; Ordered to set delay loop?
  237.             jne    request_0040    ; Jump if NO.   
  238.             mov    cs:setting,bx    ; Set delay loop counter.
  239.             mov    al,OK        ; Everything went OK.
  240.             ret 
  241.  
  242. request_0040:        mov    al,ERROR    ; Unknown function.
  243.             ret   
  244. request            endp
  245.  
  246.             ;------------------------------------------------
  247.             ;   keyboard - This routine will compare every
  248.             ;    keystroke in search of our hot key.
  249.             ;    If <Alt> <F10> is pressed then send     
  250.             ;    WHOA!'s Menu into motion.
  251.             ;------------------------------------------------
  252. keyboard        proc    far
  253.             ;--- Check for READ KEYBOARD ---
  254.  
  255.             cmp    ah,0        ; Is it a keyboard request?
  256.             je    keyboard_0010    ; Jump if YES. 
  257.             jmp    dword ptr cs:old_keyboard_int ; Do original
  258.                         ; keyboard routine.
  259.             ;--- Check for hot key ---
  260.  
  261. keyboard_0010:        pushf            ; Call original keyboard.
  262.             call    dword ptr cs:old_keyboard_int
  263.             pushf
  264.             cmp    ax,HOT_KEY    ; Is key hot key?
  265.             je    keyboard_0020    ; Jump if YES.
  266.             popf
  267.             iret            ; Return to interrupter.
  268.  
  269.             ;--- Check to see if menu is already active. ---
  270.             ;--- If not, activate menu routine. ------------
  271.  
  272. keyboard_0020:        cli            ; Stop Interrupts 
  273.             cmp    cs:activated,0    ; Already active?
  274.             jne    keyboard_0030    ; Jump if YES. 
  275.  
  276.             mov    cs:activated,1    ; Set active flag.
  277.             call    active        ; Activate menu.
  278.             mov    cs:activated,0    ; Clear active flag.
  279.  
  280.             ;--- Contine on with next key --
  281.  
  282. keyboard_0030:        sti
  283.             mov    ah,0
  284.             popf
  285.             jmp    keyboard_0010
  286. keyboard        endp
  287.  
  288.             ;-----------------------------------------------------
  289.             ;    active - This procedure protects amd initializes
  290.             ;    the system and calls the menu.
  291.             ;-----------------------------------------------------
  292. active            proc    near
  293.             mov    cs:delay_switch,0 ; Turn off delay.
  294.  
  295.             push    ax
  296.             push    bx
  297.  
  298.             ;--- Setup our stack ---
  299.  
  300.             mov    cs:save_sp,sp
  301.             mov    cs:save_ss,ss
  302.             mov    ax,offset cseg:slowdown_0010 + STACK_SIZE
  303.             mov    bx,cs
  304.             cli
  305.             mov    ss,bx
  306.             mov    sp,ax
  307.             sti
  308.  
  309.             push    cx        ; Save registers.
  310.             push    dx
  311.             push    si
  312.             push    di
  313.             push    es
  314.             push    ds
  315.             push    bp
  316.  
  317.             mov    ax,cs
  318.             mov    ds,ax
  319.  
  320.             call    calibrate    ; Call menu calibrate.
  321.  
  322.             pop    bp        ; Restore registers.
  323.             pop    ds
  324.             pop    es
  325.             pop    di
  326.             pop    si
  327.             pop    dx
  328.             pop    cx
  329.  
  330.             ;--- Restore old stack ---
  331.  
  332.             cli
  333.             mov    ss,cs:save_ss
  334.             mov    sp,cs:save_sp
  335.             sti
  336.             pop    bx
  337.             pop    ax
  338.  
  339.             mov    al,cs:enable
  340.             mov    cs:delay_switch,al ; Turn on Delay.
  341.             ret
  342. active            endp
  343.  
  344.             ;-----------------------------------------------
  345.             ;    time_of_day - This procedure contains
  346.             ;    the loop that slows the system down.
  347.             ;    The original time_of_day routine is
  348.             ;    called to maintain the system's clock.
  349.             ;-----------------------------------------------
  350. time_of_day        proc    far
  351.  
  352.             ;--- Call original time_of_day routine ---
  353.  
  354.             pushf
  355.             call    dword ptr cs:old_time_int
  356.  
  357.             ;--- Check to see if we are disabled ---
  358.  
  359.             pushf
  360.             cmp    cs:delay_switch,0 ; Are we disabled?
  361.             je    time_of_day_0030 ; Jump if YES. 
  362.  
  363.             ;--- Do our loop to the current setting ---
  364.  
  365.             cli            ; Stop all interrupts.
  366.             push    cx
  367.             mov    cx,cs:setting
  368. time_of_day_0010:    push    cx
  369.             mov    cx,TIME_PAUSE
  370. time_of_day_0020:    loop    time_of_day_0020
  371.             pop    cx
  372.             loop    time_of_day_0010
  373.             pop    cx
  374.             sti            ; Restore interrupts.
  375.  
  376.             ;--- Return to caller ---
  377.  
  378. time_of_day_0030:    popf
  379.             iret
  380. time_of_day        endp
  381.  
  382.             ;---------------------------------------------------
  383.             ;   calibrate - This procedure displays WHOA!'s
  384.             ;    menu to allow calibration of the slowdown
  385.             ;    loop and allow disable/enable of WHOA!
  386.             ;---------------------------------------------------
  387. calibrate        proc    near
  388.             call    save_screen    ; Save the current screen.
  389.             mov    ax,setting    ; Initialize our strobe setting.
  390.             mov    move_strobe1,ax
  391.             mov    move_strobe2,TIME_PAUSE / 10
  392.  
  393.             ;--- Display menu using macro ---
  394.  
  395.     DISPLAY_AT 00,00,BORDER,<'╔════════════════════════════════════════╗'>
  396.     DISPLAY_AT 00,01,BORDER,186
  397.     DISPLAY_AT 01,01,TEXT,   <'        WHOA! by Brad Crandall           '>
  398.     DISPLAY_AT 41,01,BORDER,186
  399.     DISPLAY_AT 00,02,BORDER,<'╟────────────────────────────────────────╢'>
  400.     DISPLAY_AT 00,03,BORDER,<186>
  401.     DISPLAY_AT 01,03,TEXT,   <'       Key          Meaning             '>
  402.     DISPLAY_AT 41,03,BORDER,<186>
  403.     DISPLAY_AT 00,04,BORDER,<186>
  404.     DISPLAY_AT 01,04,TEXT,   <'     -------    ----------------        '>
  405.     DISPLAY_AT 41,04,BORDER,<186>
  406.     DISPLAY_AT 00,05,BORDER,<186>
  407.     DISPLAY_AT 01,05,TEXT,   <'        +       Execute Faster          '>
  408.     DISPLAY_AT 41,05,BORDER,<186>
  409.     DISPLAY_AT 00,06,BORDER,<186>
  410.     DISPLAY_AT 01,06,TEXT,   <'        -       Execute Slower          '>
  411.     DISPLAY_AT 41,06,BORDER,<186>
  412.     DISPLAY_AT 00,07,BORDER,<186>
  413.     DISPLAY_AT 01,07,TEXT,   <'        D       Disable WHOA!           '>
  414.     DISPLAY_AT 41,07,BORDER,<186>
  415.     DISPLAY_AT 00,08,BORDER,<186>
  416.     DISPLAY_AT 01,08,TEXT,   <'        E       Enable WHOA!            '>
  417.     DISPLAY_AT 41,08,BORDER,<186>
  418.     DISPLAY_AT 00,09,BORDER,<186>
  419.     DISPLAY_AT 01,09,TEXT,   <'     <Enter>    Quit                    '>
  420.     DISPLAY_AT 41,09,BORDER,<186>
  421.     DISPLAY_AT 00,10,BORDER,<186>
  422.     DISPLAY_AT 01,10,TEXT,   <'                                        '>
  423.     DISPLAY_AT 41,10,BORDER,<186>
  424.     DISPLAY_AT 00,11,BORDER,<186>
  425.     DISPLAY_AT 01,11,TEXT,   <' Current setting 00000   ->          <- '>
  426.     DISPLAY_AT 41,11,BORDER,<186>
  427.     DISPLAY_AT 00,12,BORDER,<186>
  428.     DISPLAY_AT 01,12,TEXT,   <'         Press <ENTER> to quit.         '>
  429.     DISPLAY_AT 41,12,BORDER,<186>
  430.     DISPLAY_AT 00,13,BORDER,<'╚════════════════════════════════════════╝'>
  431.             ;--- Display delay loop number ---
  432.  
  433. calibrate_0005:        call    display_number
  434.  
  435.             ;--- Move fancy strobe ---
  436.  
  437. calibrate_0010:        dec    move_strobe2
  438.             jne    calibrate_0035
  439.             mov    move_strobe2,TIME_PAUSE / 10
  440.             dec    move_strobe1
  441.             jne    calibrate_0035
  442.             mov    ax,setting
  443.             mov    move_strobe1,ax
  444.             DISPLAY_AT strobe,11,TEXT,<' '>
  445.             cmp    strobe,STROBE_END
  446.             je    calibrate_0020
  447.             inc    strobe
  448.             jmp    calibrate_0030
  449. calibrate_0020:        mov    strobe,STROBE_BEGIN
  450. calibrate_0030:        cmp    enable,0
  451.             je    calibrate_0035
  452.             DISPLAY_AT strobe,11,BORDER,<'*'>
  453.  
  454.             ;--- Get data from keyboard if available ---
  455.  
  456. calibrate_0035:        mov    ah,1
  457.             pushf
  458.             call    dword ptr old_keyboard_int ; Original.
  459.             je    calibrate_0010    ; Jump if no data.
  460.             mov    ah,0
  461.             pushf
  462.             call    dword ptr old_keyboard_int ; Original.
  463.  
  464.             ;--- Calibrate based on character received ---
  465.  
  466.             cmp    al,'+'        ; Do we speed up?
  467.             jne    calibrate_0040    ; Jump if NO.  
  468.             dec    setting        ; Speed up loop.
  469.             cmp    setting,SET_MIN    ; Less than minimum setting?
  470.             jae    calibrate_0038    ; Jump if NO.  .
  471.             mov    setting,SET_MIN    ; Set to minimum setting.
  472. calibrate_0038:        jmp    calibrate_0005
  473.  
  474. calibrate_0040:        cmp    al,'-'        ; Do we slow down?
  475.             jne    calibrate_0050    ; Jump if NO.   
  476.             inc    setting        ; Slow down.
  477.             cmp    setting,SET_MAX    ; Greater than maximum?
  478.             jbe    calibrate_0045    ; Jump if NO.   
  479.             mov    setting,SET_MAX    ; Set to maximum setting.
  480. calibrate_0045:        jmp    calibrate_0005
  481.  
  482. calibrate_0050:        cmp    al,13        ; Are we done?
  483.             je    calibrate_0060    ; Jump if YES. 
  484.  
  485.             cmp    al,'d'        ; Disable WHOA!?
  486.             je    calibrate_0051    ; Jump if YES. 
  487.             cmp    al,'D'        ; Disable WHOA!?
  488.             jne    calibrate_0052    ; Jump if NO. 
  489. calibrate_0051:        mov    enable,0    ; Disable WHOA!
  490.             jmp    calibrate_0010
  491.  
  492. calibrate_0052:        cmp    al,'e'        ; Enable WHOA!?
  493.             je    calibrate_0053    ; Jump if YES.
  494.             cmp    al,'E'        ; Enable WHOA!?
  495.             jne    calibrate_0055    ; Jump if NO.   
  496. calibrate_0053:        mov    enable,1    ; Enable WHOA!
  497.  
  498. calibrate_0055:        jmp    calibrate_0010
  499.  
  500. calibrate_0060:        call    restore_screen    ; Restore the screen.
  501.             ret
  502. calibrate        endp
  503.  
  504.             ;--------------------------------------------------
  505.             ;    display_number - This procedure will display
  506.             ;    the current setting of the delay loop at
  507.             ;    the expected position on the screen.
  508.             ;--------------------------------------------------
  509. display_number        proc    near
  510.             mov    ax,setting
  511.             call    numasc        ; Convert to an ASCII number.
  512.  
  513.             mov    di,18        ; Set parameters.
  514.             mov    ax,11
  515.             mov    cx,TEXT
  516.             mov    si,offset cseg:asc_value
  517.  
  518.             call    display_at_proc ; Display ASCII number.
  519.  
  520.             ret
  521. display_number        endp
  522.  
  523.             ;--------------------------------------------------
  524.             ;    save_screen - This procedure will save the
  525.             ;    current screen.  Care is taken so that
  526.             ;    not only the data on the screen is saved,
  527.             ;    but the current mode, color, and palette
  528.             ;    is saved also. This allows WHOA to
  529.             ;    work under graphic conditions.
  530.             ;--------------------------------------------------
  531. save_screen        proc    near
  532.             push    ds
  533.             push    es
  534.  
  535.             ;--- Get screen information ---
  536.  
  537.             xor    ax,ax
  538.             mov    es,ax
  539.             mov    ax,es:[463h]
  540.             add    ax,6
  541.             mov    cs:crt_stat,ax    ; CRT status port address.
  542.  
  543.             mov    screen_seg,0b800h ; Screen address.
  544.             xor    ax,ax
  545.             mov    es,ax
  546.             mov    ax,word ptr es:[0410h]
  547.             and    al,30h
  548.             cmp    al,30h
  549.             jne    save_screen_0010
  550.             mov    screen_seg,0b000h ; Screen address.
  551.  
  552.             ;--- Save the screen to a safe area ---
  553.  
  554. save_screen_0010:    call    screen_off    ; Turn off the display.
  555.             mov    di,offset cseg:slowdown_0010 + STACK_SIZE + 1
  556.             mov    ax,cs
  557.             mov    es,ax
  558.             mov    ds,screen_seg
  559.             xor    si,si
  560.             mov    cx,SCREEN_SIZE
  561.             cld
  562.             rep    movsw
  563.             call    screen_on    ; Turn on the display.
  564.  
  565.             ;--- Get current Video State ---
  566.  
  567.             mov    ah,15
  568.             int    10h
  569.             mov    cs:video_state,al
  570.             mov    cs:current_page,bh
  571.             mov    ah,03
  572.             int    10h
  573.             mov    cs:cursor_location,dx
  574.  
  575.             ;--- Get current background color and palette ---
  576.  
  577.             push    es
  578.             xor    ax,ax
  579.             mov    es,ax
  580.             mov    al,es:[466h]    ; CRT_Palette.
  581.             and    al,1fh
  582.             mov    cs:current_background,al
  583.             mov    cs:current_palette,0
  584.             mov    al,es:[466h]
  585.             and    al,20h
  586.             je    save_screen_0015
  587.             mov    cs:current_palette,1
  588. save_screen_0015:    pop    es
  589.  
  590.             ;--- Change Video State to an 80 X 25 mode ---
  591.             ;--- if necessary. ---------------------------
  592.  
  593.             cmp    cs:video_state,3
  594.             jbe    save_screen_0030
  595.             cmp    cs:video_state,7
  596.             je    save_screen_0030
  597.  
  598.             mov    ah,0
  599.             mov    al,3
  600.             cmp    cs:screen_seg,0b800h
  601.             je    save_screen_0020
  602.             mov    al,2
  603. save_screen_0020:    int    10h        ; Change mode to 80 X 25.
  604.  
  605.             ;--- Move cursor off screen ---
  606.  
  607. save_screen_0030:    mov    bh,cs:current_page
  608.             mov    dx,0ffffh
  609.             mov    ah,2
  610.             int    10h
  611.  
  612.             pop    es
  613.             pop    ds
  614.             ret
  615. save_screen        endp
  616.  
  617.             ;----------------------------------------------------
  618.             ;    restore_screen - This procedure will restore
  619.             ;    the screen to the state it was in prior to
  620.             ;    the save_screen call.
  621.             ;----------------------------------------------------
  622. restore_screen        proc    near
  623.             push    ds
  624.             push    es
  625.  
  626.             ;--- Restore Video State ---
  627.  
  628.             cmp    video_state,3
  629.             jbe    restore_0010
  630.             cmp    video_state,7
  631.             je    restore_0010
  632.  
  633.             mov    al,video_state
  634.             mov    ah,0
  635.             int    10h
  636.  
  637.             ;--- Restore Background color and palette ---
  638.  
  639.             mov    ah,11
  640.             mov    bl,current_palette
  641.             mov    bh,1
  642.             int    10h
  643.  
  644.             mov    ah,11
  645.             mov    bl,current_background
  646.             mov    bh,0
  647.             int    10h
  648.  
  649.             ;--- Reset Cursor Position ---
  650.  
  651. restore_0010:        mov    bh,current_page
  652.             mov    dx,cursor_location
  653.             mov    ah,2
  654.             int    10h
  655.  
  656.             ;--- Restore screen ---
  657.  
  658.             call    screen_off    ; Turn off the display.
  659.             mov    si,offset cseg:slowdown_0010 + STACK_SIZE + 1
  660.             mov    es,screen_seg
  661.             xor    di,di
  662.             mov    cx,SCREEN_SIZE
  663.             cld
  664.             rep    movsw
  665.             call    screen_on    ; Turn on the display.
  666.  
  667.             pop    es
  668.             pop    ds
  669.             ret
  670. restore_screen        endp
  671.  
  672.             ;----------------------------------------------------
  673.             ;    display_at_proc - This procedure is used by
  674.             ;    the macro "DISPLAY_AT".  This procedure
  675.             ;    will display a string to a given location
  676.             ;    anywhere on the screen.
  677.             ;----------------------------------------------------
  678. display_at_proc        proc    near
  679.             mov    es,screen_seg
  680.             mul    columns
  681.             add    di,ax
  682.             shl    di,1        ; Screen offset.
  683.             mov    ah,cl        ; Color in ah.
  684. display_at_proc_0010:    mov    al,byte ptr cs:[si]
  685.             inc    si
  686.             cmp    al,0        ; Are we done?
  687.             je    display_at_proc_0050 ; Jump if YES. 
  688.  
  689.             ;--- Wait for horizontal retrace if CGA ---
  690.  
  691.             cmp    screen_seg,0b800h
  692.             jne    display_at_proc_0040
  693.             push    ax
  694.             mov    dx,crt_stat
  695.             cli
  696. display_at_proc_0020:    in    al,dx
  697.             test    al,1
  698.             jne    display_at_proc_0020
  699. display_at_proc_0030:    in    al,dx
  700.             test    al,1
  701.             je    display_at_proc_0030
  702.             pop    ax
  703.  
  704.             ;--- Display one byte of string ---
  705.  
  706. display_at_proc_0040:    stosw
  707.             sti
  708.             jmp    display_at_proc_0010
  709.  
  710. display_at_proc_0050:    ret
  711. display_at_proc        endp
  712.  
  713. ;------------------------------------------------------------------------------
  714. ;    NUMASC.INC    By Brad D Crandall    Created 04/23/85          |
  715. ;------------------------------------------------------------------------------
  716. ;                                          |
  717. ;    numasc:                                      |
  718. ;                                          |
  719. ;    This procedure contains the code necessary to convert a              |
  720. ;    binary value into an ASCII coded number.  This routine will not          |
  721. ;    work correctly on numbers larger than 65536. The numbers          |
  722. ;    must be integers greater than or equal to 0.                  |
  723. ;                                          |
  724. ;    How to use:                                  |
  725. ;                                          |
  726. ;        1) The following variables must be defined in your DS segment.    |
  727. ;                                          |
  728. ;        asc_value    DB    5 DUP(?)                  |
  729. ;                                          |
  730. ;        2) Load the AX register with the binary value.              |
  731. ;                                          |
  732. ;        3) Upon return, ASC_VALUE will contain the ASCII Coded number.    |
  733. ;                                          |
  734. ;        4) All registers are preserved except AX.                  |
  735. ;                                          |
  736. ;------------------------------------------------------------------------------
  737. base_ten        dw    10000,1000,100,10,1
  738. numasc            proc    near
  739.             push    si
  740.             push    cx
  741.             push    dx
  742.             push    di
  743.             xor    si,si
  744.             xor    di,di
  745.             mov    cx,5
  746. numasc_0005:        mov    dl,0
  747. numasc_0010:        sub    ax,cs:base_ten[si]
  748.             jb    numasc_0020
  749.             inc    dl
  750.             jmp    numasc_0010
  751. numasc_0020:        add    ax,cs:base_ten[si]
  752.             add    dl,'0'
  753.             mov    asc_value[di],dl
  754.             add    si,2
  755.             inc    di
  756.             loop    numasc_0005
  757.             pop    di
  758.             pop    dx
  759.             pop    cx
  760.             pop    si
  761.             ret
  762. numasc            endp
  763.  
  764.             ;-------------------------------------------------
  765.             ;    restore - This procedure will restore the
  766.             ;    interrupt vectors to the state that they
  767.             ;    were in prior to the execution of WHOA! 
  768.             ;    This terminates WHOA completely.
  769.             ;-------------------------------------------------
  770. restore            proc    near
  771.             push    ax
  772.             push    bx
  773.  
  774.             ;--- Setup our stack ---
  775.  
  776.             mov    cs:save_sp,sp
  777.             mov    cs:save_ss,ss
  778.             mov    ax,offset cseg:slowdown_0010 + STACK_SIZE
  779.             mov    bx,cs
  780.             cli
  781.             mov    ss,bx
  782.             mov    sp,ax
  783.             sti
  784.  
  785.             push    cx
  786.             push    dx
  787.             push    si
  788.             push    di
  789.             push    es
  790.             push    ds
  791.             push    bp
  792.  
  793.             ;--- Restore time_of_day interrupt to the original ---
  794.  
  795.             mov    dx,cs:word ptr cs:old_time_int
  796.             mov    ds,cs:word ptr cs:old_time_int+2
  797.             mov    ah,25h
  798.             mov    al,TIME_INT
  799.             int    21h
  800.  
  801.             ;--- Restore multiplex/slow interrupt to original state ---
  802.  
  803.             mov    dx,cs:word ptr cs:old_multi_int
  804.             mov    ds,cs:word ptr cs:old_multi_int+2
  805.             mov    ah,25h
  806.             mov    al,MULTI_INT
  807.             cmp    cs:dos_version,DOS_CMP
  808.             jae    restore2_0010
  809.             mov    al,cs:slow_interrupt
  810. restore2_0010:        int    21h
  811.  
  812.             ;--- Restore keyboard interrupt to the original ---
  813.  
  814.             mov    dx,cs:word ptr cs:old_keyboard_int
  815.             mov    ds,cs:word ptr cs:old_keyboard_int+2
  816.             mov    ah,25h
  817.             mov    al,KEYBOARD_INT
  818.             int    21h
  819.  
  820.             pop    bp
  821.             pop    ds
  822.             pop    es
  823.             pop    di
  824.             pop    si
  825.             pop    dx
  826.             pop    cx
  827.  
  828.             ;--- Restore old stack ---
  829.  
  830.             cli
  831.             mov    ss,cs:save_ss
  832.             mov    sp,cs:save_sp
  833.             sti
  834.             pop    bx
  835.             pop    ax
  836.             ret
  837. restore            endp
  838.  
  839.             ;---------------------------------------
  840.             ;    screen-off - This procedure will
  841.             ;    turn off the display. We do
  842.             ;    this so we don't have to see
  843.             ;    CGA snow during massive direct
  844.             ;    screen I/O.
  845.             ;---------------------------------------
  846. screen_off        proc    near
  847.             push    ds
  848.             xor    ax,ax
  849.             mov    ds,ax
  850.             mov    al,byte ptr ds:[0465h]
  851.             and    al,0f7h
  852.             mov    dx,03d8h
  853.             out    dx,al
  854.             pop    ds
  855.             ret
  856. screen_off        endp
  857.  
  858.             ;--------------------------------------
  859.             ;    screen_on - This procedure will 
  860.             ;    turn on the display.
  861.             ;--------------------------------------
  862. screen_on        proc    near
  863.             push    ds
  864.             xor    ax,ax
  865.             mov    ds,ax
  866.             mov    al,byte ptr ds:[0465h]
  867.             or    al,08h
  868.             out    dx,al
  869.             pop    ds
  870.             ret
  871. screen_on        endp
  872.  
  873. ;------------------------------------------------------------------------------
  874. ;           N O N - R E S I D E N T   D A T A   A R E A
  875. ;------------------------------------------------------------------------------
  876. valid_parm        db    0    ; Switch to determine valid.
  877.  
  878. ;------------------------------------------------------------------------------
  879. ;           N O N - R E S I D E N T   C O D E   A R E A
  880. ;------------------------------------------------------------------------------
  881.  
  882.             ;--- First major code to be executed within WHOA ---
  883.  
  884. slowdown_0010:        push    ds
  885.             xor    ax,ax
  886.             push    ax
  887.             mov    ax,cs
  888.             mov    ds,ax
  889.  
  890.             ;--- Get the DOS version number ---
  891.             mov    ah,30h        ; Get DOS Version number.
  892.             int    21h
  893.             xchg    al,ah
  894.             mov    dos_version,ax    ; High is MAJOR, Low is MINOR.
  895.  
  896.             call    set_loop_default ; Set the loop default counter.
  897.  
  898. DISPLAY_DOS <'Copyright 1988 COMPUTE! Publications, Inc.  All rights reserved.',13,10>
  899.  
  900.             ;--- Determine whether to install or unload ---
  901.  
  902.             cmp    dos_version,DOS_CMP ; What DOS Version??
  903.             jae    slow_down_00105    ; Jump if DOS >= 3.1.
  904.             call    check_installed
  905.             jmp    slow_down_00106
  906. slow_down_00105:    mov    ah,MULTI_HANDLE
  907.             mov    al,INSTALL_CHECK    
  908.             int    MULTI_INT
  909. slow_down_00106:    cmp    al,NOT_INSTALLED ; Are we installed?
  910.             jne    slow_down_0011     ; Jump if NO.
  911.             jmp    slow_down_0019
  912.  
  913.             ;--- If a parameter was passed, then send ----------
  914.             ;--- it to the memory resident part of WHOA! ---
  915.             ;--- otherwise disable WHOA completely. --------
  916.  
  917. slow_down_0011:        cmp    valid_parm,0    ; Any parm?
  918.             jne    slow_down_00113    ; Jump if YES. 
  919.             jmp    slow_down_0013
  920.  
  921. slow_down_00113:    cmp    valid_parm,1    ; Was it a valid parm?
  922.             je    slow_down_00115    ; Jump if YES. 
  923.  
  924.             DISPLAY_DOS <'Parameter out of range.',10,13>
  925.             DISPLAY_DOS <'Valid values are between 1 and 435.',10,13>
  926.             ret            ; Return to DOS.
  927.  
  928.             ;--- Pass new delay loop value to TSR WHOA ---
  929.  
  930. slow_down_00115:    mov    bx,setting
  931.             RESIDENT SET_LOOP
  932.             cmp    al,OK
  933.             jne    slow_down_0012
  934.             DISPLAY_DOS <'Setting completed.',10,13>
  935.             ret            ; Return to DOS.
  936. slow_down_0012:        DISPLAY_DOS <'TSR is unable to set.',10,13>
  937.             ret            ; Return to DOS.
  938.  
  939.             ;--- Uninstall WHOA ---
  940.  
  941. slow_down_0013:        RESIDENT UNINSTALL
  942.             cmp    al,OK        ; All go well?
  943.             jne    slow_down_0015    ; Jump if NO.   
  944.             DISPLAY_DOS <'Uninstalled.',10,13>
  945.             ret            ; Return to DOS.
  946. slow_down_0015:        DISPLAY_DOS <'Unable to uninstall.',10,13>
  947.             ret            ; Return to DOS.
  948.  
  949.             ;--- Install proper entry interrupt ---
  950.             ;--- based on DOS Version. ---
  951.  
  952. slow_down_0019:        cmp    dos_version,DOS_CMP
  953.             jae    slow_down_0020
  954.  
  955.             mov    al,slow_interrupt ; Install SLOW_INT.
  956.             mov    ah,35h
  957.             int    21h        ; Save original.
  958.             mov    word ptr old_multi_int,bx
  959.             mov    word ptr old_multi_int+2,es
  960.             mov    al,slow_interrupt   
  961.             mov    dx,offset cseg:interrupt
  962.             mov    ah,25h
  963.             int    21h
  964.             jmp    slow_down_0030
  965.  
  966. slow_down_0020:        mov    al,MULTI_INT    ; Install MULTI_INT
  967.             mov    ah,35h
  968.             int    21h        ; Save original.
  969.             mov    word ptr old_multi_int,bx
  970.             mov    word ptr old_multi_int+2,es
  971.             mov    dx,offset cseg:multiplex
  972.             mov    al,MULTI_INT
  973.             mov    ah,25h
  974.             int    21h
  975.  
  976.             ;--- Install Keyboard ---
  977.  
  978. slow_down_0030:        mov    al,KEYBOARD_INT
  979.             mov    ah,35h
  980.             int    21h        ; Save original.
  981.             mov    word ptr old_keyboard_int,bx
  982.             mov    word ptr old_keyboard_int+2,es
  983.  
  984.             mov    dx,offset cseg:keyboard
  985.             mov    al,KEYBOARD_INT
  986.             mov    ah,25h
  987.             int    21h
  988.  
  989.             ;--- Install time_of_day ---
  990.  
  991.             mov    al,TIME_INT
  992.             mov    ah,35h
  993.             int    21h        ; Save original.
  994.             mov    word ptr old_time_int,bx
  995.             mov    word ptr old_time_int+2,es
  996.  
  997.             mov    dx,offset cseg:time_of_day
  998.             mov    al,TIME_INT
  999.             mov    ah,25h
  1000.             int    21h
  1001.  
  1002.             ;--- Terminate and stay resident ---
  1003.  
  1004.             DISPLAY_DOS <'Now resident.  Use <Alt> F10 for calibrating.',13,10>
  1005.             mov    ax,cs
  1006.             mov    es,ax
  1007.             add    sp,4        ; Clean up stack.
  1008.             mov    dx,offset cseg:slowdown_0010 + STACK_SIZE + SCREEN_SIZE + SCREEN_SIZE + 2
  1009.             int    27h
  1010. slowdown        endp
  1011.  
  1012.             ;-----------------------------------------------------
  1013.             ;    set_loop_default - This procedure will examine
  1014.             ;    the command parameters in the Program 
  1015.             ;    Segment Prefix for any Loop Default Value.
  1016.             ;
  1017.             ;    Returns:
  1018.             ;    valid_parm is set as follows:
  1019.             ;        0 = No parameter given.
  1020.             ;        1 = Valid numeric parameter found.
  1021.             ;        2 = Valid parameter found.
  1022.             ;-----------------------------------------------------
  1023. set_loop_default    proc    near
  1024.             cmp    byte ptr es:[80h],0
  1025.             je    set_loop_default_0900
  1026.  
  1027.             ;--- Set numeric as loop default ---
  1028.  
  1029.             mov    di,81h        ; Offset of parms.
  1030.             mov    word ptr value,0
  1031.             xor    ch,ch
  1032.             mov    cl,byte ptr es:[80h]
  1033.  
  1034. set_loop_default_0010:    mov    al,byte ptr es:[di]
  1035.             inc    di
  1036.             cmp    al,'0'
  1037.             jb    set_loop_default_0020
  1038.             cmp    al,'9'
  1039.             ja    set_loop_default_0020
  1040.             call    ascnum
  1041. set_loop_default_0020:    loop    set_loop_default_0010
  1042.  
  1043.             mov    valid_parm,2    ; Set for invalid parm.
  1044.  
  1045.             ;--- Check numeric value to see if it is in range ---
  1046.  
  1047.             cmp    value,SET_MIN
  1048.             jb    set_loop_default_0910
  1049.             cmp    value,SET_MAX
  1050.             ja    set_loop_default_0910
  1051.  
  1052.             ;--- Valid value found ---
  1053.  
  1054.             mov    ax,value
  1055.             mov    setting,ax
  1056.             mov    valid_parm,1
  1057.             ret
  1058.  
  1059.             ;--- No Value Found ---
  1060.  
  1061. set_loop_default_0900:    mov    valid_parm,0
  1062. set_loop_default_0910:    ret
  1063. set_loop_default    endp
  1064.  
  1065.             ;------------------------------------------------
  1066.             ;    check_installed - This procedure will check
  1067.             ;    to see if WHOA is already resident.
  1068.             ;    This procedure is used on DOS versions
  1069.             ;    less than 3.1.
  1070.             ;
  1071.             ;    Returns in AL:
  1072.             ;    NOT_INSTALLED
  1073.             ;    INSTALLED
  1074.             ;------------------------------------------------
  1075. check_installed        proc    near
  1076.             push    es
  1077.  
  1078.             mov    ah,35h
  1079.             mov    al,slow_interrupt
  1080.             int    21h
  1081.  
  1082.             mov    al,INSTALLED
  1083.  
  1084.             ;--- Check to see routines match ---
  1085.             mov    si,offset cseg:interrupt
  1086.             mov    di,bx
  1087.             mov    cx,10        ; Number of bytes to compare.
  1088.             rep    cmpsb
  1089.             je    check_installed_0010
  1090.             mov    al,NOT_INSTALLED
  1091.  
  1092. check_installed_0010:    pop    es
  1093.             ret
  1094. check_installed        endp
  1095.  
  1096.             ;-----------------------------------------------
  1097.             ;    call_interrupt - This procedure calls the
  1098.             ;    resident portion of WHOA for DOS
  1099.             ;-----------------------------------------------
  1100. call_interrupt        proc    near
  1101.             int    SLOW_INT
  1102.             ret
  1103. call_interrupt        endp
  1104.  
  1105. ;------------------------------------------------------------------------------
  1106. ;    ASCNUM.INC    By Brad D Crandall    Created 04/23/85          |
  1107. ;------------------------------------------------------------------------------
  1108. ;                                          |
  1109. ;    ascnum:                                      |
  1110. ;                                          |
  1111. ;    This procedure contains the code necessary to convert a              |
  1112. ;    ASCII coded number into a binary value.  This routine will not          |
  1113. ;    work correctly on numbers larger than 65536. The number              |
  1114. ;    must be integers greater than or equal to 0.                  |
  1115. ;                                          |
  1116. ;    How to use:                                  |
  1117. ;                                          |
  1118. ;        1) Move a zero to VALUE.                          |
  1119. ;                                          |
  1120. ;        MOV    VALUE,0                              |
  1121. ;                                          |
  1122. ;        2) Load the AL register with the left most digit and then call    |
  1123. ;           this routine. Do this for every digit in the number.          |
  1124. ;                                          |
  1125. ;        MOV    AL,'3'        ; Translate 34 into binary.          |
  1126. ;        CALL    NUMASC                              |
  1127. ;                                          |
  1128. ;        MOV    AL,'4'                              |
  1129. ;        CALL    NUMASC                              |
  1130. ;                                          |
  1131. ;        3) The binary value will be found in the variable VALUE          |
  1132. ;                                          |
  1133. ;        MOV    AX,VALUE    ; AX now contains binary 34.          |
  1134. ;                                          |
  1135. ;        4) All registers are preserved except AX.                  |
  1136. ;------------------------------------------------------------------------------
  1137. ten            dw    10
  1138. value            dw    ?
  1139.  
  1140. ascnum            proc    near
  1141.             push    dx
  1142.             cmp    al,'0'
  1143.             jb    ascnum_0010     ; Not a number.
  1144.             cmp    al,'9'
  1145.             ja    ascnum_0010    ; Not a number.
  1146.             sub    al,'0'
  1147.             xor    ah,ah
  1148.             mov    dx,ax
  1149.             push    dx
  1150.             mov    ax,cs:value
  1151.             mul    cs:ten
  1152.             pop    dx
  1153.             add    ax,dx
  1154.             mov    cs:value,ax
  1155. ascnum_0010:        pop    dx
  1156.             ret
  1157. ascnum            endp
  1158.  
  1159. cseg            ends
  1160.             end    slowdown
  1161.